
class XRegExp {
    constructor(source, flag, root = "root") {
        this.table = new Map()
        this.regExp = new RegExp(this.compileRegExp(source, root, 0).source, flag)
        //console.log(this.regExp)
        //console.log(this.table)
    }

    compileRegExp(source, name, start) {
        if (source[name] instanceof RegExp)
            return {
                source: source[name].source,
                length: 0
            }

        let length = 0


        let regExp = source[name].replace(/\<([^>]+)\>/g, (str, $1) => {
            this.table.set(start + length, $1)
            this.table.set($1, start + length)
            ++length
            let r = this.compileRegExp(source, $1, start + length)
            length += r.length
            return "(" + r.source + ")"
        })
        return { source: regExp, length: length }
    }
    exec(string) {
        let r = this.regExp.exec(string)
        for (let i = 1; i < r.length; i++) {
            if (r[i] !== void 0) {
                r[this.table.get(i - 1)] = r[i]
                //console.log(this.table.get(i-1))
            }
        }
       //console.log(r[0])
        return r
    }

    get lastIndex() {
        return this.regExp.lastIndex
    }

    set lastIndex(value) {
        return this.regExp.lastIndex = value
    }


}


export function* scan(str) {
    //let regExp = / |\n|<comments>|<token>/
    let regExp = new XRegExp({
        InputElement: "<WhiteSpace>|<LineTerminator>|<Comments>|<Token>",
        WhiteSpace: / /,
        LineTerminator: /\n/,
        Comments: /\/\*(?:[^*]|\*[^\/])*\*\//,
        Token: "<Literal>|<Keywords>|<Identifier>|<Punctuator>",
        Literal: "<NumericLiteral>|<BooleanLiteral>|<StringLiteral>|<NullLiteral>",
        NumericLiteral: /(?:[1-9][0-9]*|0)(?:\.[0-9]*)?|\.[0-9]+/,
        BooleanLiteral: /true|false/,
        StringLiteral: /\"(?:[^"\n]|\\[\s\S])*\"|\'(?:[^'\n]|\\[\s\S])*\'/,
        NullLiteral: /null/,
        Identifier: /[a-zA-Z_$][a-zA-Z0-9_$]*/,
        Keywords: /if|else|for|fuction|var|let/,
        Punctuator: /\(|=>|=|\<|\+\+|==|\)|\*|\.|\{|\}|\[|\]|;|:/

    }, "g", "InputElement")
    while (regExp.lastIndex < str.length) {
        let r = regExp.exec(str)
        //console.log(r[0])
        if (r.WhiteSpace) {

        } else if (r.LineTerminator) {

        } else if (r.Comments) {

        } else if (r.NumericLiteral) {
            yield {
                type: "NumericLiteral",
                value: r[0]
            }
        } else if (r.BooleanLiteral) {
            yield {
                type: "BooleanLiteral",
                value: r[0]
            }
        } else if (r.StringLiteral) {
            yield {
                type: "StringLiteral",
                value: r[0]
            }
        } else if (r.NullLiteral) {
            yield {
                type: "NullLiteral",
                value: null
            }
        } else if (r.Identifier) {
            yield {
                type: "Identifier",
                name: r[0]
            }
        } else if (r.Keywords) {
            yield {
                type: r[0]
            }
        } else if (r.Punctuator) {
            yield {
                type: r[0]
            }
        } else {
            throw new Error("unexpected token " + r[0])
        }

        if (!r[0].length)
            break
    }
    yield {
        type: "EOF"
    }
}


let source = (`http.createServer((request, response) => {
    let body = [];
    request.on('error', (err) => {
        console.error(err);
    }).on('data',(chunk) => {
        body.push(chunk.toString());
    }).on('end',()=>{
        body = Buffer.concat([Buffer.from(body.toString())]).toString();
        console.log("body:",body);
        response.writeHead(200,{'Content-Type':'text/html'});
        response.end('hellw world\n');
    })
}).listen(8088);`)






